// // 装饰器， 平时用不到，装饰器是基于类的。  mobx   nestjs主要就是靠装饰器来实现的

// // 装饰器是一个试验性语法，说明后续的具体的实现 可能会发生变化

// // 什么是装饰器 ， 本质就是函数， 只能在类和类的成员上使用
// // ts装饰器：
// // 1.类装饰器、
// // 2.（静态方法，原型方法）方法装饰器

// // 后面我们不会独立使用
// // 3.（静态属性、静态方法）属性装饰器、
// // 4. (get 、 set)访问符号装饰器
// // 5. (原型函数、构造函数)参数装饰器

// function ClassDecorator(target: any) {
//   // target 指向的是类本身
//   target.type = "动物";
//   target.getType = function () {
//     return this.type;
//   };
//   target.prototype.eat = function () {
//     console.log("animal eat");
//   };
// }
// function OverrideDecorator(target: any) {
//   return class extends target {
//     eat() {
//       // 重写animal类
//       super.eat();
//       console.log("child eat");
//     }
//   };
// }
// /*
// @OverrideDecorator
// @ClassDecorator
// class Animal {}

// console.log((Animal as any).type, (Animal as any).getType());
// const animal = new Animal();
// (animal as any).eat();
// */

// // 方法装饰器

// function Enum(isEnm: boolean) {
//   // 类的原型
//   // 属性名
//   // 属性访问器
//   return function (target: any, key: string, descriptor: PropertyDescriptor) {
//     // 装饰器函数
//     descriptor.enumerable = isEnm;
//     let original = descriptor.value;

//     descriptor.value = function (...args: any[]) {
//       console.log("prev");
//       original.call(this, ...args);
//       console.log("next");
//     };
//   };
// }

// function toUpperCase(target: any, key: string) {
//   // 我门不会采用属性访问器来扩展
//   let value = "";
//   Object.defineProperty(target, key, {
//     // 给原型添加属性
//     // 属性劫持？
//     get() {
//       return value;
//     },
//     set(newVal) {
//       value = newVal.toUpperCase();
//     },
//   });
// }

// // class Animal {
// //   name = 'abc'
// // }
// // class Animal {
// //   constructor(){
// //     this.name = 'abc'
// //   }
// // }
// class Animal {
//   @Enum(true)
//   eat() {
//     console.log("eat");
//   }
//   @toUpperCase
//   public name = "abc"; // 给实例添加属性
// }
// // 如果编译成esNext Animal {name=abc} 默认此值就在函数上 ， 但是如果变成es2015 那么此值是在new的时候才负值的
// const animal = new Animal();
// // animal.name = "bbb";
// console.log(animal.name);

// // 对类 和  方法进行装饰是有意义的
// /*
// class A {
//   a = 1;
// }
// class A {
//   constructor() {
//     this.a = 1;
//   }
// }*/

export {};

// 装饰器就是一个函数，只能在类中使用(类本身，类成员使用)
// 装饰器是一个试验性语法，说明后续的具体的实现 可能会发生变,使用时需要开启 "experimentalDecorators": true,
// 装饰器的分类: 类的装饰器，方法装饰器（静态方法，原型方法，属性装饰器（静态属性、静态方法），访问装饰器(get 、 set)，参数装饰器(原型函数、构造函数)

// 1),类的装饰器 给类来进行扩展属性和方法，也可以返回一个子类去重写父类
// 一般不通过装饰器去扩展类型的属性和方法，
// 因为扩展后原来没有的方法，无法正常访问到，例如Animal as any).getType  animal.drink();需要通过namespace和interface，来进行扩展
// target 指向的是类本身，扩展属性和方法
const classDecorator = <T extends new (...args: any[]) => any>(target: T) => {
  // 静态属性
  (target as any).type = "动物";
  // 静态方法
  (target as any).getType = function () {
    return this.type;
  };
  // 原型方法
  Object.assign(target.prototype, { eat() {}, drink() {} });
};
// target 指向的是类本身，返回一个子类去重写父类，这个用的少
function OverrideDecorator(target: any) {
  // 重写animal类
  return class extends target {
    eat() {
      super.eat();
      console.log("child eat");
    }
  };
}

/* 
// 扩展class Animal，属性和方法
// 需要一个和class Animal同名的接口
interface Animal {
  eat(): void;
  drink(): void;
}

class Animal {
  eat() {
    console.log("动物eats");
  }
} */
@classDecorator
class Animal1 {
  eat() {
    console.log("动物eats");
  }
}
// Animal.getType，无法正常访问到getType，需要(Animal as any).getType
console.log((Animal1 as any).getType);
// animal.drink(); // 无法正常访问到drink
const animal = new Animal1();

//2)方法装饰器
//除了描述类和静态方法之外target都是原型，类和静态方法指代的是类本身
function Enum(isEnum: boolean): MethodDecorator {
  return function (target, propertyKey, desriptor) {
    // desrigtor.enumerable // 是否可枚举// desriptor.writable // 是否能被重写
    // desriptor.configurable // 是否属性能被删除
    // desriptor.value // 当前函数的值
    desriptor.enumerable = isEnum;
    let original = desriptor.value as any;
    desriptor.value = function () {
      console.log("prev eat");
      return original(...arguments);
    } as any;
  };
}
//3)属性装饰器
function ToUpper(isUpper: boolean): PropertyDecorator {
  return function (target, propertyKey) {
    // 是先装饰器解析，还是先赋值 。不同的es版本会有不同的解析
    // 这个地方貌似是给原型添加了个属性，如果在es2015 先装饰器解析 ,设置原型属性，后续赋值的时候会触发原型属性，导致初始化时有大写，属性没有值
    // 如果在esnext中 先赋值, 再装饰器解析，导致初始化时没有大写，属性有值
    let val = "";
    Object.defineProperty(target, propertyKey, {
      enumerable: true,
      get() {
        return val.toUpperCase();
      },
      set(newValue) {
        val = newValue;
      },
    });
  };
}
// 4) 访问装饰器 get set
function valToUpper(target: any, key: string, descriptor: any) {
  let originalSet = descriptor.set;
  let originalGet = descriptor.get;
  descriptor.set = function (newVal: string) {
    return originalSet.call(this, newVal.toUpperCase());
  };
  descriptor.get = function (newVal: string) {
    return originalGet.call(this) + "abc";
  };
}

class Animal2 {
  @ToUpper(true)
  public name: string = "animal";
  @Enum(true) // 最终装饰器必须返回一个函数,控制是否可遍历
  eat() {
    console.log("动物 original");
  }

  private _val!: string;
  @valToUpper
  get val() {
    return this._val;
  }
  //   @valToUpper get set 只能一个上面有装饰器
  set val(newVal: string) {
    this._val = newVal;
  }
}

const animal2 = new Animal2();
animal2.eat();
// 调用set方法
animal2.val = "abc";
// 调用get方法
console.log(animal2.val);

// pnpm 不会拍平  node_modules
// npm i 会拍平 node_modules，配置config   module解析模式   // "moduleResolution": "node10",
